using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace TAAlert.CommonUtils
{
    class MACDEvaluator
    {
        static private readonly TraceSwitch traceM = new TraceSwitch("MACDEvaluator", "Debug info for MACD Evaluator class");

        /// <summary>Calculate today's value of MACDHistogram based on yeasterday's EMA's and today's value</summary>
        /// <param name="value1">Todays value (price)</param>
        /// <param name="nShort">Short EMA period</param>
        /// <param name="emaShort0">Yesterday's value of EMAshort (price the first day)</param>
        /// <param name="emaShort1">Return today's value of EMAshort</param>
        /// <param name="nLong">Long EMA period</param>
        /// <param name="emaLong0">Yeasterday's value of EMAlong (price the first day)</param>
        /// <param name="emaLong1">Return today's value of EMAlong</param>
        /// <param name="nFast">Fast line EMA period</param>
        /// <param name="macdSig0">Yesterday's value of signal line (0 the first day)</param>
        /// <param name="macdSig1">Return today's value of the signal line</param>
        /// <returns></returns>
        public static double calcMacdHistogram(double value1,
                                                int nShort, double emaShort0, out double emaShort1,
                                                int nLong, double emaLong0, out double emaLong1,
                                                int nFast, double macdSig0, out double macdSig1)
        {
            emaShort1 = EMAEvaluator.calcEMA(emaShort0, nShort, value1);
            emaLong1 = EMAEvaluator.calcEMA(emaLong0, nLong, value1);

            double macdLine1 = emaShort1 - emaLong1;
            macdSig1 = EMAEvaluator.calcEMA(macdSig0, nFast, macdLine1);
            return macdLine1 - macdSig1;
        }

        /// <summary>Calculate MACD histogram array</summary>
        /// <param name="value">List of prices</param>
        /// <param name="nEMAShort">short EMA period</param>
        /// <param name="nEMALong">long EMA period</param>
        /// <param name="nFast">FAST line period (macd Signal period)</param>
        /// <returns>time series for macdLine - macdSignal</returns>
        public static List<double> calcMacdHistogram(IList<double> value, int nEMAShort, int nEMALong, int nFast)
        {
            double emaShortLast, emaLongLast, macdSigLast;
            List<double> macdHist = MACDEvaluator.calcMacdHistogram(value, nEMAShort, nEMALong, nFast, out emaShortLast, out emaLongLast, out macdSigLast);
            return macdHist;
        }

        /// <summary>Calculate MACD histogram array. Return last values of EMA's and MACDsig. Used for alerts</summary>
        /// <param name="value">List of prices</param>
        /// <param name="nEMAShort">short EMA period</param>
        /// <param name="nEMALong">long EMA period</param>
        /// <param name="nFast">FAST line period (macd Signal period)</param>
        /// <param name="emaShortLast">Return the last value of emaShort</param>
        /// <param name="emaLongLast">Return the last value of emaLong</param>
        /// <param name="macdSigLast">Return the last value of macdSig</param>
        /// <returns>time series for macdLine - macdSignal</returns>
        public static List<double> calcMacdHistogram(IList<double> value, int nEMAShort, int nEMALong, int nFast, out double emaShortLast, out double emaLongLast, out double macdSigLast)
        {
            Debug.WriteLineIf(traceM.TraceInfo, "--> calcMacdHistogram(value.count=" + value.Count + ", nShort=" + nEMAShort + ", nLong=" + nEMALong + ", nFast=" + nFast + ")");
            Debug.Indent();

            int n = value.Count;
            List<double> macdHist = new List<double>(n);
            double emaShort0 = n > 0 ? value[0] : 0;
            double emaLong0 = n > 0 ? value[0] : 0;
            double macdSig0 = 0;
            emaShortLast = emaShort0;
            emaLongLast = emaLong0;
            macdSigLast = macdSig0;

            Debug.WriteLineIf(traceM.TraceVerbose, "ix,value,emaShort,emaLong,macdSig,macdHist"); 
            for (int i = 0; i < n; ++i)
            {
                double hist = calcMacdHistogram(value[i], nEMAShort, emaShort0, out emaShortLast, nEMALong, emaLong0, out emaLongLast, nFast, macdSig0, out macdSigLast);
                macdHist.Add(hist);
                emaShort0 = emaShortLast;
                emaLong0 = emaLongLast;
                macdSig0 = macdSigLast;
                Debug.WriteLineIf(traceM.TraceVerbose, i + "," + value[i] + "," + emaShortLast + "," + emaLongLast + "," + macdSigLast + "," + hist);
            }

            Debug.Unindent();
            return macdHist;
        }

        /// <summary>Give signal based on histogram sign at index ix.</summary>
        /// <param name="macdHistogram">MACD histogram</param>
        /// <param name="ix">date index in MACD list</param>
        /// <returns>BUY if macdHist is positive, SELL if macdHist is negative</returns>
        public static Signal macdSignal(List<double> macdHistogram, int ix)
        {
            return MACDEvaluator.macdSignal(macdHistogram[ix]);
        }

        /// <summary>Give signal based on histogram sign at index ix.</summary>
        /// <param name="macdHistogram">The latest value of macdHistogram</param>
        public static Signal macdSignal(double macdHistogram)
        {
            Signal result;
            if (macdHistogram > 0)
                result = Signal.Buy;
            else if (macdHistogram < 0)
                result = Signal.Sell;
            else
                result = Signal.Neutral;
            return result;
        }

        /// <summary>Using input time series calculate MACDline, MACDsignal and MACD histogram</summary>
        /// <param name="value">Time series (prices) = base for MACD calculations</param>
        /// <param name="nEMAShort">period for short EMA average</param>
        /// <param name="nEMALong">period for long EMA average</param>
        /// <param name="nFast">period for FAST EMA average</param>
        /// <param name="macdLine">time series with EMA(nShort, value)-EMA(nLong, value)</param>
        /// <param name="macdSignal">time series with EMA(nFast, MACDline) </param>
        /// <param name="macdHistogram">time series for macdLine - macdSignal</param>
        public static void calculateMacdLines(IList<double> value, int nEMAShort, int nEMALong, int nFast,
                                out List<double> macdLine, out List<double> macdSignal, out List<double> macdHistogram)
        {
            int n = value.Count;
            // 1. Calculate MACDLine
            macdLine = new List<double>(n);
            List<double> emaShort = EMAEvaluator.calcEMAArray(value, nEMAShort);
            List<double> emaLong = EMAEvaluator.calcEMAArray(value, nEMALong);
            for (int i = 0; i < n; ++i)
                macdLine.Add(emaShort[i] - emaLong[i]);

            // 2. Calculate MACDSignal
            macdSignal = EMAEvaluator.calcEMAArray(macdLine, nFast);

            // 3. Calculate MACDHistogram
            macdHistogram = new List<double>(n);
            for (int i = 0; i < n; ++i)
                macdHistogram.Add(macdLine[i] - macdSignal[i]);
        }

        // ---> Unused functions <---

        /// <summary>Convenience function. Return just MACD historgram</summary>
        /// <param name="value">Time series (prices) = base for MACD calculations</param>
        /// <param name="nEMAShort">period for short EMA average</param>
        /// <param name="nEMALong">period for long EMA average</param>
        /// <param name="nFast">period for FAST EMA average</param>
        /// <returns>time series for macdLine - macdSignal</returns>
        public static List<double> calcMacdHistogramDEPRECIATED(IList<double> value, int nEMAShort, int nEMALong, int nFast)
        {
            int n = value.Count;
            List<double> macdLine;
            List<double> macdSig;
            List<double> macdHist;
            calculateMacdLines(value, nEMAShort, nEMALong, nFast, out macdLine, out macdSig, out macdHist);

            return macdHist;
        }
    }
}
